﻿using System;

using Microsoft.Xna.Framework;

namespace SmlEngine
{
    /// <summary>
    /// Defines a right triangle.
    /// </summary>
    public struct RightTriangle
    {
        private Vector2 angle, across, up;

        /// <summary>
        /// The origin of the right triangle.
        /// </summary>
        public Vector2 Origin { get; set; }

        /// <summary>
        /// Returns if the triangle is sloped.
        /// </summary>
        public bool IsSloped { get; set; }

        /// <summary>
        /// The X-coordinate of the origin.
        /// </summary>
        public float X
        {
            get { return Origin.X; }
            set { Origin = new Vector2(value, Origin.Y); }
        }

        /// <summary>
        /// The Y-coordinate of the origin.
        /// </summary>
        public float Y
        {
            get { return Origin.Y; }
            set { Origin = new Vector2(Origin.X, value); }
        }

        public Vector2 Up
        {
            get { return up + Origin; }
            set { up = value - Origin; }
        }

        public Vector2 Across
        {
            get { return across + Origin; }
            set { across = value - Origin; }
        }

        public Vector2 Angle
        {
            get { return angle; }
            set { angle = value; }
        }

        public float Left { get { return Math.Min(Up.X, Math.Min(Angle.X, Across.X)); } }
        public float Bottom { get { return Math.Min(Up.Y, Math.Min(Angle.Y, Across.Y)); } }
        public float Right { get { return Math.Max(Up.X, Math.Max(Angle.X, Across.X)); } }
        public float Top { get { return Math.Max(Up.Y, Math.Max(Angle.Y, Across.Y)); } }

        public bool Inverted
        {
            get { return Up.Y > Angle.Y; }
        }

        public Gradient Gradient
        {
            get { return IsSloped ? Gradient.Flat : new Gradient(Math.Abs(Up.X - Across.X), Math.Abs(Up.Y - Across.Y)); }
        }

        public BoundingRectangle Bounds
        {
            get { return new BoundingRectangle(X, Y, Width, Height); }
        }

        public float Width
        {
            get { return Right - Left; }
        }

        public float Height
        {
            get { return Top - Bottom; }
        }

        public float Slope
        {
            get
            {
                return Height / Width
                * Angle.X < Right ? 1 : -1
                * Angle.Y > Bottom ? 1 : -1;
            }
        }

        public RightTriangle(Vector2 angle, Vector2 across, Vector2 up)
            : this()
        {
            Angle = angle;
            Across = across;
            Up = up;
            IsSloped = true;
        }

        public RightTriangle(float x, float y, float width, float height)
            : this(x, y, width, height, true) { }

        public RightTriangle(float x, float y, float width, float height, bool isSloped)
            : this()
        {
            Origin = new Vector2((width - x) / 2, (height - y) / 2);
            Up = new Vector2(width / 2, x / 2);
            Across = new Vector2();

            IsSloped = isSloped;
        }

        /// <summary>
        /// Calculates the signed depth of intersection between this triangle and a rectangle.
        /// </summary>
        /// <returns>
        /// The amount of overlap between this triangle and a rectangle. These
        /// depth values can be negative depending on which side the rectangle
        /// intersects. This allows callers to determine the correct direction 
        /// to push objects in order to resolve collisions. If the rectangle is 
        /// not intersecting, zero is returned.
        /// </returns>
        public float GetIntersectionDepth(BoundingRectangle rectangle)
        {
            if (IsSloped)
            {
                Vector2 origin = new Vector2(rectangle.Left + rectangle.Width / 2f, rectangle.Bottom);

                if (origin.X < Left ||
                    origin.X > Right ||
                    origin.Y < Bottom ||
                    origin.Y > Top)
                    return 0; // Early out

                float height = Up.Y - Slope * Up.X;

                if (Angle.Y <= Bottom)
                {
                    float y = origin.X * Slope + height;
                    if (rectangle.Bottom <= y)
                    {
                        return y - rectangle.Bottom;
                    }
                }
                else
                {
                    float y = origin.X * Slope + height;
                    if (rectangle.Bottom >= y)
                    {
                        return y - rectangle.Bottom;
                    }
                }

                return 0;
            }
            else
            {
                Vector2 depth = Bounds.GetIntersectionDepth(rectangle);
                return Math.Min(depth.X, depth.Y);
            }
        }
    }
}
